﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace block_explorer
{
    class SSAO : IPostProcess
    {
        private DeferredRenderer mRenderer;

        private RenderTarget2D mSSAORT;
        private RenderTarget2D mSSAOBlurRT;

        private Texture2D mRandNormalMap;

        private SSAOEffect mEffect;

        public bool Enabled { get; set; }
        public Texture2D Surface { get { return mSSAORT; } }

        public SSAO(DeferredRenderer renderer)
        {
            mRenderer = renderer;
        }

        public void Initialize(GraphicsDevice device, ContentManager content)
        {
            int bufferWidth = device.PresentationParameters.BackBufferWidth;
            int bufferHeight = device.PresentationParameters.BackBufferHeight;

            mSSAORT = new RenderTarget2D(device, bufferWidth, bufferHeight, false, SurfaceFormat.Color, DepthFormat.None);
            mSSAOBlurRT = new RenderTarget2D(device, bufferWidth, bufferHeight, false, SurfaceFormat.Color, DepthFormat.None);

            mRandNormalMap = content.Load<Texture2D>("RandNormalMap");

            mEffect = new SSAOEffect(device, content);
        }

        public void PrepareScene(GraphicsDevice device)
        {
            Vector2 halfBufferHalfPixel = new Vector2(0.5f / mRenderer.GBuffer.Depth.Width, 0.5f / mRenderer.GBuffer.Depth.Height);
            Vector2 gBufferPixelSize = new Vector2(0.5f / mRenderer.GBuffer.Normal.Width, 0.5f / mRenderer.GBuffer.Normal.Height);

            device.SetRenderTarget(mSSAORT);

            mEffect.Stage = SSAOEffect.Stages.SSAO;
            mEffect.RandomSize = 100.0f;
            mEffect.ScreenSize = new Vector2(mRenderer.GBuffer.Normal.Width, mRenderer.GBuffer.Normal.Height);
            mEffect.SampleRadius = 0.5f;
            mEffect.Intensity = 1.0f;
            mEffect.Scale = 0.7f;
            mEffect.Bias = 0.001f;
            mEffect.FarClip = mRenderer.Scene.SelectedCamera.Far;
            mEffect.DepthMap = mRenderer.GBuffer.Depth;
            mEffect.NormalMap = mRenderer.GBuffer.Normal;
            mEffect.RandomMap = mRandNormalMap;
            mEffect.InverseProjection = Matrix.Invert(mRenderer.Scene.SelectedCamera.Projection);

            mEffect.Apply();
            Util.QuadRenderComponent.Instance.Render(-1 * Vector2.One, Vector2.One);

            device.BlendState = BlendState.Opaque;
            {
                mEffect.Stage = SSAOEffect.Stages.Blur;

                Vector2 tempBufferRes = new Vector2(mSSAORT.Width, mSSAORT.Height);

                device.SetRenderTarget(mSSAOBlurRT);
                device.Clear(Color.Black);

                mEffect.SSAOMap = mSSAORT;
                mEffect.BlurDirection = Vector2.UnitX / mSSAORT.Width;
                mEffect.Apply();
                Util.QuadRenderComponent.Instance.Render(-1 * Vector2.One, Vector2.One);

                device.SetRenderTarget(mSSAORT);
                device.Clear(Color.Black);

                mEffect.SSAOMap = mSSAOBlurRT;
                mEffect.BlurDirection = Vector2.UnitY / mSSAORT.Width;
                mEffect.Apply();
                Util.QuadRenderComponent.Instance.Render(-1 * Vector2.One, Vector2.One);
            }
        }

        public void Draw(GraphicsDevice device)
        {
            BlendState blendState = new BlendState();
            blendState.ColorSourceBlend = Blend.DestinationColor;
            blendState.AlphaSourceBlend = Blend.DestinationAlpha;
            blendState.ColorBlendFunction = BlendFunction.Add;
            blendState.AlphaBlendFunction = BlendFunction.Add;
            blendState.ColorDestinationBlend = Blend.Zero;
            blendState.AlphaDestinationBlend = Blend.Zero;
            device.BlendState = blendState;

            mEffect.Stage = SSAOEffect.Stages.Blend;
            mEffect.BlurMap = mSSAORT;
            mEffect.Apply();
            Util.QuadRenderComponent.Instance.Render(-1 * Vector2.One, Vector2.One);
        }
    }
}
